/*
 * sr_ref.c - SR referenece type
 * Clunix, cezanne@clunix.com, 2005.3
 *
 * Copyright (c) 2001-2005 by Clunix Inc. All Rights Reserved.
 */
#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ecm.h"
#include "sr.h"
#include "sr_p.h"

ECM_INLINE static sr_t *
deref_LNP(const sr_t *sr, const sr_t *sr_LNP)
{
	sr_t	*sr_traversed, *sr_res;

	sr_traversed = traverse_sr(sr, sr_LNP, NULL, NULL, 0);

	sr_res = (sr_t *)sr_get_ptr(sr_first(sr_traversed));
	sr_free(sr_traversed);
	return sr_res;
}

ECMBOOL
srt_is_null_target(srt_t srt)
{
	if (srt_is_ref(srt) && (srt & ~SR_REF) == ST_NULL)
		return ECM_TRUE;
	return ECM_FALSE;
}

ECMBOOL
srt_is_valid_target(srt_t srt, srt_t srt_target)
{
	if (!srt_is_ref(srt))
		return ECM_FALSE;
	if ((srt & ~SR_REF) == ST_NULL)
		return ECM_TRUE;
 	if ((srt & ~SR_REF) == srt_target)
		return ECM_TRUE;
	if (srt_is_inherited(srt_target, srt & ~SR_REF))
		return ECM_TRUE;
	return ECM_FALSE;
}

ECMBOOL
srt_is_ref(srt_t srt)
{
	if (srt & (SR_VHMS | SR_VIRTUAL))
		return ECM_FALSE;
	if ((srt & SR_HOMOSET)) {
		if (srt & SR_REFHMS)
			return ECM_TRUE;
		return ECM_FALSE;
	}
	if (srt & SR_REF)
		return ECM_TRUE;
	return ECM_FALSE;
}

srt_t
srt_get_ref_type(const srt_t srt)
{
	if (srt & (SR_VHMS | SR_VIRTUAL | SR_HOMOSET | SR_REF))
		return ST_NULL;
	return (srt | SR_REF);
}

srt_t
srt_get_target_type(const srt_t srt)
{
	if (!srt_is_ref(srt))
		return ST_NULL;
	return (srt & ~SR_VIRTUAL & ~SR_REF);
}

ECMBOOL
sr_is_ref(const sr_t *sr)
{
	if (sr == NULL)
		return ECM_FALSE;
	return srt_is_ref(sr->type);
}

ECMBOOL
sr_is_valid_ref(const sr_t *sr)
{
	if (sr_is_ref(sr) && sr->srv_ptr != NULL)
		return ECM_TRUE;
	return ECM_FALSE;
}

sr_t *
sr_set_target(sr_t *sr, const sr_t *sr_target)
{
	sr_t	*sr_LNP;
	char	*ref_LNP;

	if (!sr_is_ref(sr))
		return NULL;

	ref_LNP = sr_get_LNP(sr_target);
	sr_LNP = get_sr_LNP(ref_LNP);
	if (sr_set_validref(sr, sr_LNP)) {
		ecm_free(ref_LNP);
		return sr;
	}
	sr_free(sr_LNP);
	ecm_free(ref_LNP);
	return NULL;
}

sr_t *
sr_set_ref(sr_t *sr, sr_t *sr_LNP)
{
	if (!sr_is_ref(sr))
		return NULL;

	if (sr_LNP && deref_LNP(sr->parent, sr_LNP) == NULL)
		return NULL;
	sr_free((sr_t *)sr->srv_ptr);
	sr->srv_ptr = sr_LNP;
	return sr;
}

sr_t *
sr_set_ref_LNPstr(sr_t *sr, const char *fmt, ...)
{
	va_list	ap;
	sr_t	*sr_LNP = NULL;

	if (fmt != NULL) {
		char	*buf;

		va_start(ap, fmt);
		ecm_vasprintf(&buf, fmt, ap);
		va_end(ap);
		sr_LNP = get_sr_LNP(buf);
		ecm_free(buf);
	}

	sr = sr_set_ref(sr, sr_LNP);
	if (sr == NULL)
		sr_free(sr_LNP);
	return sr;
}

sr_t *
sr_set_validref(sr_t *sr, sr_t *sr_LNP)
{
	sr_t	*sr_target = NULL;

	if (!sr_is_ref(sr))
		return NULL;
	if (sr_LNP && (sr_target = deref_LNP(sr->parent, sr_LNP)) == NULL)
		return NULL;
	if (sr_target && !srt_is_valid_target(sr->type, sr_target->type))
		return NULL;
	sr_free((sr_t *)sr->srv_ptr);
	sr->srv_ptr = sr_LNP;
	return sr;
}

const sr_t *
sr_get_ref(const sr_t *sr)
{
	if (!sr_is_ref(sr))
		return NULL;

	return (const sr_t *)sr->srv_ptr;
}

sr_t *
sr_get_deref(const sr_t *sr)
{
	sr_t		*sr_deref, *sr_LNP;

	if (!sr_is_ref(sr))
		return NULL;

	sr_LNP = (sr_t *)sr->srv_ptr;
	if (sr_LNP == NULL)
		return NULL;
	sr_deref = deref_LNP(sr->parent, sr_LNP);
	if (sr_deref == NULL)
		return NULL;
	if (!srt_is_valid_target(sr->type, sr_deref->type) &&
	    (sr->type & ~SR_REF) != ST_NULL)
		return NULL;
	return sr_deref;
}

void
sr_free_ref(ecm_ptr_t ptr)
{
	sr_free((sr_t *)ptr);
}

int
srt_ref_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	char		*LNPstr;
	ecm_size_t	reqsize;

	LNPstr = string_LNP((sr_t *)srv.ptr);
	reqsize = ecm_strlen(LNPstr);
	if (*pbuflen < reqsize) {
		*pbuflen = reqsize;
		ecm_free(LNPstr);
		return -1;
	}
	ecm_memcpy(buf, LNPstr, reqsize);
	ecm_free(LNPstr);
	return reqsize;
}

ECMBOOL
srt_ref_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	sr_t	*sr_LNP;

	if (*asrv == '\0') {
		psrv->ptr = NULL;
		return ECM_TRUE;
	}
	
	sr_LNP = get_sr_LNP(asrv);
	if (sr_LNP == NULL)
		return ECM_FALSE;
	psrv->ptr = sr_LNP;
	return ECM_TRUE;
}

